package demo.impl.ipv4;

import java.net.Inet4Address;
import java.util.concurrent.atomic.AtomicInteger;

import org.pcap4j.packet.IpV4Packet;
import org.pcap4j.packet.IpV4Rfc1349Tos;
import org.pcap4j.packet.UnknownPacket;
import org.pcap4j.packet.namednumber.EtherType;
import org.pcap4j.packet.namednumber.IpNumber;
import org.pcap4j.packet.namednumber.IpV4TosPrecedence;
import org.pcap4j.packet.namednumber.IpV4TosTos;
import org.pcap4j.packet.namednumber.IpVersion;

import demo.api.EthernetService;
import demo.api.IPv4Service;
import demo.impl.AbstractProtocolService;
import demo.util.AssertUtils;

/**
 * IPV4协议
 * 
 * @author caohj
 * @date 2017年8月5日
 */
public class IPv4ServiceImpl extends AbstractProtocolService<IpV4Packet> implements IPv4Service {

	/** 网络信息，延迟获取 */
	private volatile NetworkInfo networkInfo;

	/** IP数据报的标识字段 ，每次发送+1 */
	private AtomicInteger identifier = new AtomicInteger((int) System.currentTimeMillis());

	public IPv4ServiceImpl(EthernetService ethernet) {
		super(ethernet);
		AssertUtils.notNull(ethernet, "参数底层协议不能为空！");
	}

	@Override
	protected Number getProtocolType() {
		return EtherType.IPV4.value();
	}

	private NetworkInfo getNetworkInfo() {
		if (networkInfo == null) {
			EthernetService baseProtocol = getBaseProtocal();
			networkInfo = NetworkInfo.loadNetworkInfo(this, baseProtocol.getNetworkInterface());
		}
		return networkInfo;
	}

	@Override
	public void sendPacket(Inet4Address ip, IpNumber protocol, byte[] data) {
		AssertUtils.notNull(ip, "参数ip不能为空！");
		AssertUtils.notNull(protocol, "参数protocol不能为空！");
		AssertUtils.notNull(data, "参数data不能为空！");

		if (!isBound()) {
			throw new RuntimeException("无法发送Packet，尚未绑定底层服务！ip=" + ip + ", protocol=" + protocol);
		}

		try {
			IpV4Packet.Builder builder = new IpV4Packet.Builder();
			builder.version(IpVersion.IPV4);
			// tos实际中从未使用过
			builder.tos(new IpV4Rfc1349Tos.Builder().precedence(IpV4TosPrecedence.IMMEDIATE)
					.tos(IpV4TosTos.MINIMIZE_DELAY).build());
			builder.identification((short) identifier.incrementAndGet());
			builder.moreFragmentFlag(false).dontFragmentFlag(false).fragmentOffset((short) 0);
			builder.ttl((byte) 0xFF);
			builder.protocol(protocol);
			builder.srcAddr((Inet4Address) getNetworkInfo().getLocalhost()).dstAddr(ip);
			builder.payloadBuilder(new UnknownPacket.Builder().rawData(data));
			builder.correctChecksumAtBuild(true).correctLengthAtBuild(true).paddingAtBuild(true);

			IpV4Packet packet = builder.build();
			EthernetService baseProtocol = getBaseProtocal();
			if (getNetworkInfo().isSameSubnet(ip)) {
				// 使用ARP协议获取目标MAC地址
				// 投机取巧：本网段内也发送到网关，由网关再转一次
				baseProtocol.sendPacket(getNetworkInfo().getGatewayMac(), EtherType.IPV4, packet.getRawData());
			} else {
				// 发送到网关
				baseProtocol.sendPacket(getNetworkInfo().getGatewayMac(), EtherType.IPV4, packet.getRawData());
			}
		} catch (Exception e) {
			throw new RuntimeException("发送失败！destip=" + ip + ", protocao=" + protocol + ", msg=" + e.getMessage(), e);
		}
	}

	@Override
	protected Number getHighLevelProtocolType(IpV4Packet packet) {
		return packet.getHeader().getProtocol().value();
	}
}
